//你这个学期必须选修 numCourse 门课程，记为 0 到 numCourse-1 。 
//
// 在选修某些课程之前需要一些先修课程。 例如，想要学习课程 0 ，你需要先完成课程 1 ，我们用一个匹配来表示他们：[0,1] 
//
// 给定课程总量以及它们的先决条件，请你判断是否可能完成所有课程的学习？ 
//
// 
//
// 示例 1: 
//
// 输入: 2, [[1,0]] 
//输出: true
//解释: 总共有 2 门课程。学习课程 1 之前，你需要完成课程 0。所以这是可能的。 
//
// 示例 2: 
//
// 输入: 2, [[1,0],[0,1]]
//输出: false
//解释: 总共有 2 门课程。学习课程 1 之前，你需要先完成​课程 0；并且学习课程 0 之前，你还应先完成课程 1。这是不可能的。 
//
// 
//
// 提示： 
//
// 
// 输入的先决条件是由 边缘列表 表示的图形，而不是 邻接矩阵 。详情请参见图的表示法。 
// 你可以假定输入的先决条件中没有重复的边。 
// 1 <= numCourses <= 10^5 
// 
// Related Topics 深度优先搜索 广度优先搜索 图 拓扑排序 
// 👍 560 👎 0

package leetcode.editor.cn;

import java.util.*;

//Java：课程表
public class P207CourseSchedule {

    /**
     *
     * 思路：深度优先和广度优先， 我这边用的是广度优先。先创建有向图，然后计算每个点的入度，一开始先将入度为0的放进队列，然后循环
     *      取队列，然后将对应的子课程的入度-1, 如果子课程的度==0，加入队列。 结束时判断是否进入队列的 == 总课程数
     *
     * 执行用时： 12 ms , 在所有 Java 提交中击败了 21.26% 的用户 内存消耗： 40.5 MB , 在所有 Java 提交中击败了 31.03% 的用户
     *
     */
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public boolean canFinish(int numCourses, int[][] prerequisites) {
            Map<Integer, Set<Integer>> graph = createGraph(prerequisites);     //图
            int[] degree = createDegreeArr(numCourses, graph);     //入度
            Queue<Integer> queue = new LinkedList<>();      // 队列
            for (int i = 0; i < degree.length; i++) {
                if (degree[i] == 0) {
                    queue.add(i);
                }
            }
            if (queue.isEmpty()) {
                return false;
            }
            int i = 0;
            while (!queue.isEmpty()) {
                i++;
                int top = queue.poll();
                if (graph.containsKey(top)) {
                    // 入度 - 1
                    Set<Integer> set = graph.get(top);
                    for (Integer b : set) {
                        degree[b] -= 1;
                        if (degree[b] == 0) {
                            queue.add(b);
                        }
                    }
                }
            }
            return i == numCourses;
        }

        // 建有向图
        private Map<Integer, Set<Integer>> createGraph(int[][] prerequisites) {
            Map<Integer, Set<Integer>> map = new HashMap<>();
            for (int[] temp : prerequisites) {
                if (map.get(temp[1]) == null) {
                    map.put(temp[1], new HashSet<>());
                }
                map.get(temp[1]).add(temp[0]);
            }
            return map;
        }

        // 初始化各个课程的入度
        private int[] createDegreeArr(int numCourses, Map<Integer, Set<Integer>> graph) {
            int[] degree = new int[numCourses];     //入度
            for (Integer key : graph.keySet()) {
                Set<Integer> set = graph.get(key);
                for (Integer a : set) {
                    degree[a] += 1;
                }
            }
            return degree;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    public static void main(String[] args) {
        Solution solution = new P207CourseSchedule().new Solution();
        int[][] pre = new int[][]{};

        System.out.println(solution.canFinish(4, pre));
        // TO TEST
    }

}